﻿#pragma once
#include "AsCoreGlobal.h"
#include <QThread>

class AsWorkThreadPrivate;

class AS_CORE_EXPORT AsWorkThread : public QThread {
	Q_OBJECT

public:
	enum Flag {
		NoFlag = 0x00,
		Loop = 0x01,
		AutoExecute = 0x02,
		StartDirectly = 0x04,
		FinishedDelete = 0x08,
		Debug = 0x10,
		Common = StartDirectly | Loop | AutoExecute,
		OnceRun = StartDirectly | FinishedDelete | AutoExecute
	};

public:
	explicit AsWorkThread(QObject* parent);
	AsWorkThread(int flag = Common, QObject* parent = nullptr);
	AsWorkThread(QString threadName, QObject* parent = nullptr);
	AsWorkThread(QString threadName, int flag = Common, QObject* parent = nullptr);
	~AsWorkThread();

private:
	AsWorkThreadPrivate* mp;

private:
	void addWork(void* work);

private:
	void run() override;

public:
	bool isRunning();
	bool isFinished();

public:
	void start();
	void stop();
	void exec();
	void quit();
	void quit_block();
	void terminate();
	void terminate_block(unsigned long time = ULONG_MAX);
	void waitWorkBegin();
	void waitWorkDone();

public:
	void clearBind();
	void clearBindWorkDone();
	void clearBindFinished();

public:
	template <typename _Slot>
	void addWork(_Slot slot) {
		connect(this, &AsWorkThread::sigExecute, this, slot, Qt::DirectConnection);
		addWork((void*)&slot);
	}
	template <typename _Slot>
	void addWork(const QObject* object, _Slot slot) {
		connect(this, &AsWorkThread::sigExecute, object, slot, Qt::DirectConnection);
		addWork((void*)&slot);
	}
	template <typename _Slot>
	void addWork(const typename QtPrivate::FunctionPointer<_Slot>::Object* object, _Slot slot) {
		connect(this, &AsWorkThread::sigExecute, object, slot, Qt::DirectConnection);
		addWork((void*)&slot);
	}
	template <typename _Slot>
	void bindWorkDone(const typename QtPrivate::FunctionPointer<_Slot>::Object* object, _Slot slot) {
		connect(this, &AsWorkThread::sigWorkDone, object, slot, Qt::QueuedConnection);
	}
	template <typename _Slot>
	void bindWorkDone(const QObject* object, _Slot slot) {
		connect(this, &AsWorkThread::sigWorkDone, object, slot, Qt::QueuedConnection);
	}
	template <typename _Slot>
	void bindFinished(const typename QtPrivate::FunctionPointer<_Slot>::Object* object, _Slot slot) {
		connect(this, &AsWorkThread::sigFinished, object, slot, Qt::QueuedConnection);
	}
	template <typename _Slot>
	void bindFinished(const QObject* object, _Slot slot) {
		connect(this, &AsWorkThread::sigFinished, object, slot, Qt::QueuedConnection);
	}
	template <typename _Slot>
	void rebindWorkDone(const typename QtPrivate::FunctionPointer<_Slot>::Object* object, _Slot slot) {
		clearBindWorkDone();
		connect(this, &AsWorkThread::sigWorkDone, object, slot, Qt::QueuedConnection);
	}
	template <typename _Slot>
	void rebindWorkDone(const QObject* object, _Slot slot) {
		clearBindWorkDone();
		connect(this, &AsWorkThread::sigWorkDone, object, slot, Qt::QueuedConnection);
	}
	template <typename _Slot>
	void rebindFinished(const typename QtPrivate::FunctionPointer<_Slot>::Object* object, _Slot slot) {
		clearBindFinished();
		connect(this, &AsWorkThread::sigFinished, object, slot, Qt::QueuedConnection);
	}
	template <typename _Slot>
	void rebindFinished(const QObject* object, _Slot slot) {
		clearBindFinished();
		connect(this, &AsWorkThread::sigFinished, object, slot, Qt::QueuedConnection);
	}

public:
	template <typename _Slot>
	static AsWorkThread* run(_Slot slot) {
		AsWorkThread* workThead = new AsWorkThread(Flag::OnceRun);
		workThead->addWork(slot);
		return workThead;
	}
	template <typename _Slot>
	static AsWorkThread* run(const typename QtPrivate::FunctionPointer<_Slot>::Object* object, _Slot slot) {
		AsWorkThread* workThead = new AsWorkThread(Flag::OnceRun);
		workThead->addWork(object, slot);
		return workThead;
	}

signals:
	void sigStarted();
	void sigExecute();
	void sigWorkDone();
	void sigFinished();

};
